import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class GamePanelTest extends JComponent{
	
	private static int BORDERTHICKNESS = 5; // Thickness of the borders around the game
	private static int WORMSIZE = 15; //Width of the worm/snake
	private int SCOREFIELD = 40; // Height of the field below the game were the current score is
	private int XMIN = BORDERTHICKNESS; //
	private int YMIN = BORDERTHICKNESS;
	private static int CANVASWIDTH = (25*WORMSIZE)+(2*BORDERTHICKNESS);
	private static int CANVASHEIGHT = (20*WORMSIZE)+(2*BORDERTHICKNESS);
	private int XMAX = CANVASWIDTH-2*BORDERTHICKNESS;
	private int YMAX = CANVASHEIGHT-(2*BORDERTHICKNESS+SCOREFIELD);
	
	private int DELAY = 100; //time between worm-moves in ms.
	private int DIRECTION;
	private int OLDDIRECTION;
	private int appleX;
	private int appleY;
	private int SCORE = 0;
	private int MOVES = 0;
	
	private static CustomListener listener;
	private LinkedList<Integer> wormX;
	private LinkedList<Integer> wormY;
	Graphics2D g2d;
	
	public static void main(String[] args){
		JFrame frame = new JFrame();
		frame.setPreferredSize(new Dimension(CANVASWIDTH+20,CANVASHEIGHT+40));
		
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		frame.setLayout(new BorderLayout());
		JPanel canvasPanel = new JPanel(new BorderLayout());
		//listener = new CustomListener();
		//frame.addKeyListener(listener);
		
		frame.pack();
		canvasPanel.add(new GamePanelTest(), BorderLayout.CENTER);
		canvasPanel.setPreferredSize(new Dimension(CANVASWIDTH+20, CANVASHEIGHT));
		frame.pack();
		frame.add(canvasPanel, BorderLayout.CENTER);
		//frame.setContentPane(new GamePanelTest());
		frame.pack();
	}
	
	public GamePanelTest(){
		super();
		setSize(CANVASWIDTH+20, CANVASHEIGHT+20);
		setVisible(true);
		setDoubleBuffered(true);
		setPreferredSize(new Dimension(CANVASWIDTH+20,CANVASHEIGHT+20));
				
		setFocusable(true);
		requestFocusInWindow();
		listener = new CustomListener();
		addKeyListener(listener);
		new GameThread(listener);
	}
	
	private class GameThread extends Thread{
		
		private final CustomListener listener;
		//private int DELAY_K = 100;
		
		public GameThread(CustomListener listener){
			this.listener = listener;
			start();
			
		}
		
		public void run(){
			wormX = new LinkedList<Integer>();
			wormY = new LinkedList<Integer>();
			generateWorm();
			generateApple();

			boolean run = true;
			while(run){
				try {
					Thread.sleep(DELAY);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				OLDDIRECTION = DIRECTION;
				int newDirection = listener.getLastKeyUsed();
				if((newDirection == 37 && OLDDIRECTION == 39) || (newDirection == 39 && OLDDIRECTION == 37) || (newDirection == 38 && OLDDIRECTION == 40) || (newDirection == 40 && OLDDIRECTION == 38)){
					DIRECTION = OLDDIRECTION;
				}else{
					DIRECTION = newDirection;
				}
				run = moveWorm();
				repaint();
			}
			System.out.println("slut");
		}
	}
	
	public void paint(Graphics g) {
		super.paint(g);
        g2d = (Graphics2D) g;
        
		g2d.setPaint(Color.GRAY); // Draw the border
		g2d.fill(new Rectangle2D.Double(0, 0, CANVASWIDTH, CANVASHEIGHT));
		g2d.setPaint(Color.WHITE);
		g2d.fill(new Rectangle2D.Double(BORDERTHICKNESS, BORDERTHICKNESS, XMAX, YMAX));

		g2d.setPaint(Color.BLACK);
		Iterator<Integer> itY = wormY.iterator();
		Iterator<Integer> itX = wormX.iterator();		
		while(itX.hasNext() && itY.hasNext()){
			int tempX = itX.next();
			int tempY = itY.next();
			g2d.fill(new Rectangle2D.Double(tempX, tempY, WORMSIZE, WORMSIZE));
		}
        
		g2d.setPaint(Color.RED);
		g2d.fill(new Rectangle2D.Double(XMAX, YMAX, BORDERTHICKNESS, BORDERTHICKNESS)); // Draw apple
		g2d.fill(new Rectangle2D.Double(appleX, appleY, WORMSIZE, WORMSIZE)); // Draw apple
		g2d.drawString("SCORE : "+SCORE, 40, YMAX+15); // Print score
        g2d.dispose();
    }
	
	/**
	 * Moves the worm one step in the last valid direction typed.
	 * Checks for crashes with walls, the snake itself and apples.
	 * @return
	 */
	private boolean moveWorm(){
		MOVES++;
		int headX = wormX.getFirst();
		int headY = wormY.getFirst();
		
		if(headX < XMIN || headY < YMIN || headX > XMAX || headY > YMAX){
			return false;
		}
		
		if(DIRECTION == 37){
			headX -=WORMSIZE;
		}else if(DIRECTION == 38){
			headY -=WORMSIZE;
		}else if(DIRECTION == 39){
			headX +=WORMSIZE;
		}else if(DIRECTION == 40){
			headY +=WORMSIZE;
		}
		
		if(checkForCrash(headX, headY) == true){
			if(MOVES > 0){
				return false;
			}
		}
		
		wormX.addFirst(headX);
		wormY.addFirst(headY);

		if(headX != appleX || headY != appleY){
			wormX.removeLast();
			wormY.removeLast();
		}else{
			SCORE++;
			generateApple();
		}
		
		return true;
	}
	
	/**
	 * Checks if the pair of coordinates in the parameters match a pair of coordinates in the worm.
	 * @param x-coordinate
	 * @param y-coordinate
	 * @return 
	 */
	private boolean checkForCrash(int x, int y){
		Iterator<Integer> itX = wormX.iterator();
		Iterator<Integer> itY = wormY.iterator();
		
		
		while(itX.hasNext() && itY.hasNext()){
			int tempX = itX.next();
			int tempY = itY.next();
			if(x == tempX && y == tempY){
				System.out.println("x: " + x + " y: " + y);
				return true;
			}
		}
		return false;
	}
	
	/**
     * Waits for a specified number of milliseconds before finishing.
     * This provides an easy way to specify a small delay which can be
     * used when producing animations.
     * @param  milliseconds  the number 
     */
    public void wait(int milliseconds)
    {
        try
        {
            Thread.sleep(milliseconds);
        } 
        catch (InterruptedException e)
        {
            // ignoring exception at the moment
        }
    }
    
	/**
	 * Generates a worm so the player has a worm to start with.
	 * Basically adds coordinates to the worm-variables.
	 */
	private void generateWorm(){
		wormX.add((3*WORMSIZE)+BORDERTHICKNESS);
		wormY.add((4*WORMSIZE)+BORDERTHICKNESS);
		
		wormX.add((2*WORMSIZE)+BORDERTHICKNESS);
		wormY.add((4*WORMSIZE)+BORDERTHICKNESS);
		
		wormX.add((1*WORMSIZE)+BORDERTHICKNESS);
		wormY.add((4*WORMSIZE)+BORDERTHICKNESS);
	}
	
	/**
	 * Generates an apple and then draws it.
	 */
	private void generateApple(){
		Random random = new Random();
		do{
			appleX = XMIN + WORMSIZE*random.nextInt(XMAX/WORMSIZE);
			appleY = YMIN + WORMSIZE*random.nextInt(YMAX/WORMSIZE);
			//apple = new Coord(XMIN + WORMSIZE*random.nextInt(XMAX/WORMSIZE), YMIN + WORMSIZE*random.nextInt(YMAX/WORMSIZE));
		}while(checkForCrash(appleX, appleY) == true);
	}	

}
